import java.math.BigDecimal;
import java.util.Arrays;

/**
 * @author lichenxi
 * @date 2020/4/2
 */
public class Matrix {

     public Double[][] values;

     //是否是方阵
    public static boolean isSquMatrix(Matrix matrix){
        return matrix.colNum()==matrix.rowNum();
    }

    //获取单位矩阵 n维度
    public static Matrix getIdentity(int n){
        Double[][] doubles = new Double[n][n];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                if (j==i){
                    doubles[i][j] = 1.0;
                }else{
                    doubles[i][j] = 0.0;
                }

            }
        }
        return new Matrix(doubles);
    }

    //处理精度
    public Matrix proAcc(int acc,int type){
        for (int i = 0; i < this.rowNum(); i++) {
            for (int j = 0; j < this.rowVector(i).getlen(); j++) {
                BigDecimal bigDecimal = new BigDecimal(this.values[i][j] );
                this.values[i][j] = bigDecimal.setScale(acc,type).doubleValue();
            }
        }
        return this ;
    }

    //矩阵的逆
    public Matrix inverseMatrix(){
        if(Matrix.isSquMatrix(this)){
            Matrix identity = Matrix.getIdentity(this.rowNum());
            Vector[] colVector = new Vector[this.colNum()+identity.colNum()];
            for (int i = 0; i < this.colNum(); i++) {
                colVector[i] = this.colVector(i);
            }
            for (int i = 0; i < identity.colNum(); i++) {
                colVector[identity.colNum()+i] = identity.colVector(i);
            }
            Matrix matrix = new Matrix(colVector, false);
            Matrix matrix1 = matrix.gaussJordanElimination();
            Vector[] vectors = new Vector[this.colNum()];
            for (int i = this.colNum(),j = 0; i <matrix1.colNum(); i++,j++) {
                vectors[j] = matrix1.colVector(i);
            }
            return new Matrix(vectors,false);
        }else{
            throw new RuntimeException("方阵才有逆矩阵");
        }

    }

    //搞死约旦消元法
    public Matrix gaussJordanElimination(){
        //主元化1 除第一行其他主元前项为0 (高斯消元) 由上至下
        //除第最后一行 行其他主元后项为0 (约旦消元) 由下至上
        return this.forward().backeard();
    }

    //搞死约翰消元法的2个私有方法
    private Matrix forward(){

        for (int i = 0; i < this.rowNum(); i++) {
            //寻找当前主元最大的行并与当前行交换位置
            if (i<maxRow(i)){
                this.exchangeRow(i,maxRow(i));
            }
            if (this.values[i][i]==0){
                //下一行为 [0,0,0] 的时候
            }else {
                Vector vector = this.rowVector(i).divNumber(this.values[i][i]);
                for (int j = 0; j < this.rowVector(i).getlen(); j++) {
                    this.values[i][j] = vector.getItem(j);
                }
                for (int k = i + 1; k < this.rowNum(); k++) {
                    Vector vector1 = this.rowVector(i).mulNumber(this.values[k][i]);
                    Vector vector2 = this.rowVector(k).subVector(vector1);
                    for (int j = 0; j < this.rowVector(i).getlen(); j++) {
                        this.values[k][j] = vector2.getItem(j);
                    }
                }
            }

        }
        return this;
    }

    private Matrix backeard(){

        for (int i = this.rowNum()-2; i >-1 ; i--) {
            for (int k = i+1; k < this.rowNum(); k++) {

                Vector vector1 = this.rowVector(k).mulNumber(this.values[i][k]);
                Vector vector2 = this.rowVector(i).subVector(vector1);

                for (int j = i + 1; j < this.rowVector(i).getlen(); j++) {
                    this.values[i][j] = vector2.getItem(j);
                }
            }
        }
        return this;
    }

    //获取当前位置主元最大的行
    private int maxRow(int index){
        if (index>this.colNum()){
            throw new RuntimeException("主元下标越界");
        }
        int maxrow = 0;
        for (int i = 1; i < this.rowNum(); i++) {
            if (this.getItem(i-1,index)>this.getItem(i,index));
        }
        return maxrow;
    }

    //交换矩阵行
    public void exchangeRow(int index,int index2){
        if (!(index<this.rowNum()&&index2<this.rowNum())){
            throw new RuntimeException("行数越界");
        }
        Double[] row1 = Arrays.copyOf(this.rowVector(index).values, this.rowVector(index).getlen());
        Double[] row2 = Arrays.copyOf(this.rowVector(index2).values, this.rowVector(index2).getlen());
        for (int i = 0; i < this.colNum(); i++) {
            this.values[index2][i] = row1[i];
            this.values[index][i] = row2[i];
        }
    }
    //交换矩阵列
    public void exchangeCol(int index,int index2){
        if (!(index<this.colNum()&&index2<this.colNum())){
            throw new RuntimeException("列数越界");
        }
        Double[] col1 = Arrays.copyOf(this.colVector(index).values, this.colVector(index).getlen());
        Double[] col2 = Arrays.copyOf(this.colVector(index2).values, this.colVector(index2).getlen());
        for (int i = 0; i < this.colNum(); i++) {
            this.values[i][index2] = col1[i];
            this.values[i][index] = col2[i];
        }
    }

    //矩阵的乘法(不满足交换律)
    public Matrix mulMatrix(Matrix matrix){
        //矩阵a的行数必须和矩阵b的列数相同
        if(this.colNum()!=matrix.rowNum()){
            throw new RuntimeException("矩阵维度不相同");
        }
        //矩阵乘法的结果矩阵的每一列 可以看成矩阵a乘以 矩阵b的每一列组成 我们得到所有的列向量够贱结果矩阵
        Vector[] vectors = new Vector[this.colNum()];
        for (int i = 0; i < matrix.colNum(); i++) {
            Vector vector = matrix.colVector(i);
            vectors[i] = this.mulVector(vector);
        }
        return new Matrix(vectors,false);
    }

     //矩阵的转置
     public Matrix flipMatrix(){
         Vector[] vectors = new Vector[this.colNum()];
         for (int i = 0; i < this.colNum(); i++) {
             vectors[i] = this.colVector(i);
         }
         return new Matrix(vectors,true);
     }

     //零矩阵
     public static Matrix zero(int row,int col){
         Double[][] doubles = new Double[row][col];
         for (int i = 0; i < doubles.length; i++) {
             for (int j = 0; j < doubles[i].length; j++) {
                 doubles[i][j] = 0.0;
             }
         }
         return new Matrix(doubles);
     }

     //矩阵和向量的乘法
    public Vector mulVector(Vector vector){
        if (this.colNum()!=vector.getlen()){
            throw new RuntimeException("维度不相同");
        }
        Double[] doubles = new Double[this.rowNum()];
        //相当于结果向量的每一行值等于矩阵对应行向量和此向量的点乘
        for (int i = 0; i < this.rowNum(); i++) {
            doubles[i] = this.rowVector(i).pointMul(vector);
        }
        return new Vector(doubles);
    }

     //向量的数量除法
     public Matrix divNumber(Double number) {
        return this.mulNumber(1/number);
     }

         //矩阵的数量乘法
    public Matrix mulNumber(Double number){
        Double[][] doubles = new Double[this.rowNum()][this.colNum()];
        for (int i = 0; i < doubles.length; i++) {
            for (int j = 0; j < doubles[i].length; j++) {
                doubles[i][j] = this.values[i][j]*number;
            }
        }
        return new Matrix(doubles);
    }

     //矩阵的反向
    public Matrix revMatrix(){
        Double[][] doubles = new Double[this.rowNum()][this.colNum()];
        for (int i = 0; i < doubles.length; i++) {
            for (int j = 0; j < doubles[i].length; j++) {
                doubles[i][j] = -this.values[i][j];
            }
        }
        return new Matrix(doubles);
    }

     //矩阵的减法
    public Matrix subMatrix(Matrix matrix){
        return this.addMatrix(matrix.revMatrix());
    }

     //矩阵的加法
    public Matrix addMatrix(Matrix matrix){
        if (this.rowNum()!=matrix.rowNum()||this.colNum()!=matrix.colNum()){
            throw new RuntimeException("矩阵维度不相同");
        }
        Double[][] doubles = new Double[this.rowNum()][this.colNum()];
        for (int i = 0; i < doubles.length; i++) {
            for (int j = 0; j < doubles[i].length; j++) {
                doubles[i][j] = this.values[i][j]+matrix.values[i][j];
            }
        }
        return new Matrix(doubles);
    }

     //返回第n行的列向量
    public Vector colVector(int index){
        Double[] doubles = new Double[this.rowNum()];
        for (int i = 0; i < this.rowNum(); i++) {
            doubles[i] = this.values[i][index];
        }
        return new Vector(doubles);
    }

     //返回第n行的行向量
    public Vector rowVector(int index){
        return new Vector(this.values[index]);
    }
    public Vector[] colVectors(){
        Vector[] vectors = new Vector[this.colNum()];
        for (int i = 0; i < this.colNum(); i++) {
            vectors[i] = this.colVector(i);
        }
        return vectors;
    }

    public Vector[] rowVectors(){
        Vector[] vectors = new Vector[this.rowNum()];
        for (int i = 0; i < this.rowNum(); i++) {
            vectors[i] = this.rowVector(i);
        }
        return vectors;
    }

     //获取矩阵位置的元素
    public Double getItem(int row,int col){
        return this.values[row][col];
    }

    //获取矩阵中的元素个数
    public Integer size(){
        return this.colNum()*rowNum();
    }

    //获取矩阵的行数
    public Integer rowNum(){
        return this.values.length;
    }

    //获取矩阵的列数
    public Integer colNum(){
        return this.values[0].length;
    }

    //参数有效性交给使用者
    //行够贱
    Matrix(Double[][] values){
        this.values = Arrays.copyOf(values, values.length);
    }
    //参数有效性交给使用者
    //使用向量够贱
    Matrix(Vector[] vectors, boolean bol){
        if (bol==true){
            //使用行向量够贱
            this.values = new Double[vectors.length][vectors[0].getlen()];
            for (int i = 0; i <vectors.length ; i++) {
                for (int j = 0; j < vectors[i].getlen(); j++) {
                    this.values[i][j] = vectors[i].getItem(j);
                }
            }
        }else{
            //使用列向量够贱
            this.values = new Double[vectors[0].getlen()][vectors.length];
            for (int i = 0; i <vectors.length ; i++) {
                for (int j = 0; j < vectors[i].getlen(); j++) {
                    Double item = vectors[i].getItem(j);
                    this.values[j][i] = item;
                }
            }
        }

    }

    public void printMatrix(){
            for (int i = 0; i <this.values.length ; i++) {
                String value = "";
                value += "[";
                for (int j = 0; j <this.values[i].length ; j++) {
                    value+=this.values[i][j];
                    if (j<this.values[i].length-1){
                        value+=",";
                    }
                }
                value += "]";
                System.out.println(value);
            }
    }
    @Override
    public String toString() {
        String value = "";
        for (int i = 0; i <this.values.length ; i++) {
            value += "[";
            for (int j = 0; j <this.values[i].length ; j++) {
                value+=this.values[i][j];
                if (j<this.values[i].length-1){
                    value+=",";
                }
            }
            value += "]";
            if (i<this.values.length-1){
                value+=",";
            }
        }
        return "Matrix{" +
                "values=[" + value +
                "]}";
    }
}
